﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using System.Diagnostics;
using ThreeByte.ActiveDeck.Config;
using System.Collections.ObjectModel;

namespace ThreeByte.ActiveDeck.Data.UserResponse
{
    [UserData(ShortTypeName = "ShortAnswer")]
    public class ShortAnswerUserData : AggregateUserData
    {

        private readonly object _responseLock = new object();
        public ObservableCollection<string> Answers { get; private set; }

        public string TopAnswer {
            get {
                lock(_responseLock) {
                    return Answers.FirstOrDefault();
                }
            }
        }

        public bool Repeat {  //Defaults to false
            get;
            set;
        }

        private ShortAnswerUserData() {
            Answers = new ObservableCollection<string>();
            Answers.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(Answers_CollectionChanged);
        }

        protected ShortAnswerUserData(XElement node): this() {

            Page = int.Parse(node.Attribute("Page").Value);

            foreach(XElement a in node.Elements(ns + "Answer")) {
                Answers.Add(a.Attribute("Text").Value);
            }
        }

        public ShortAnswerUserData(int page) : this() {
            Page = page;
        }

        public override XElement ToXml() {
            XElement node = base.ToXml();

            foreach(string s in Answers) {
                node.Add(new XElement(ns + "Answer", new XAttribute("Text", s)));
            }
            return node;
        }

        //Update the Answer values and return true if anything changed
        public override bool UpdateUserData(AggregateUserData newUserData) {
            bool modified = !(string.IsNullOrWhiteSpace(TopAnswer));
            
            if(!(newUserData is EmptyUserData)) {
                //Do a better job of comparing
                ShortAnswerUserData newData = (ShortAnswerUserData)newUserData;
                lock(_responseLock) {
                    if(Answers.SequenceEqual(newData.Answers)) {
                        modified = false;
                    } else {
                        modified = true;
                        Answers.Clear();
                        foreach(string a in newData.Answers) {
                            Answers.Add(a);
                        }
                    }
                }
            } else {
                //Empty User Data
                lock(_responseLock) {
                    Answers.Clear();
                }
            }
            return modified;
        }

        public static new ShortAnswerUserData ParseXml(XElement node) {
            return new ShortAnswerUserData(node);
        }

        void Answers_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) {
            Refresh();
        }

        private void Refresh() {
            //Get Total Responses
            lock(_responseLock) {
                TotalResponses = Answers.Count;
            }
        }

        private List<string> returnedResponses = new List<string>();

        /// <summary>
        /// Returns the next available string or null if none is available
        /// </summary>
        /// <returns></returns>
        public string GetNext() {

            List<string> currentAnswers;
            lock(_responseLock) {
                currentAnswers = Answers.ToList();
            }

            if(currentAnswers.Count < 1) {
                return null;  //Nothing to return
            }

            //Find the next string in the answers list that is not in the returnedResponses list
            foreach(string s in currentAnswers) {
                if(!(returnedResponses.Contains(s))) {
                    returnedResponses.Add(s);
                    return s;
                }
            }

            //If Repeat is enabled, and there are no answers to return, clear the list and search again
            if(Repeat) {
                returnedResponses.Clear();
            }
            foreach(string s in currentAnswers) {
                if(!(returnedResponses.Contains(s))) {
                    returnedResponses.Add(s);
                    return s;
                }
            }

            //Otherwise return null
            return null;
        }

        public bool HasNext() {
            List<string> currentAnswers;
            lock(_responseLock) {
                currentAnswers = Answers.ToList();
            }

            if(currentAnswers.Count < 1) {
                return false;  //Nothing to return
            } else if(Repeat) {
                return true;  //If there are some answers and Repeat is set then yes
            }

            //Find the next string in the answers list that is not in the returnedResponses list
            foreach(string s in currentAnswers) {
                if(!(returnedResponses.Contains(s))) {
                    return true;
                }
            }

            //Otherwise return null
            return false;
        }
    }
}
